package com.fastjrun.security.config;

import com.fastjrun.security.authentication.CustomAuthenticationFilter;
import com.fastjrun.security.authentication.mobile.MobileAuthenticationConfig;
import com.fastjrun.security.authentication.mobile.MobileValidateFilter;
import com.fastjrun.security.authentication.wechat.WechatAuthenticationConfig;
import com.fastjrun.security.authentication.wechat.WechatValidateFilter;
import com.fastjrun.security.authorize.AuthorizeConfigurerManager;
import com.fastjrun.security.properties.SecurityProperties;
import com.fastjrun.security.utils.WhiteUrlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;

import javax.annotation.Resource;
import javax.sql.DataSource;


@ComponentScan("com.fastjrun.security")
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    Logger logger = LoggerFactory.getLogger(getClass());

    @Bean
    public PasswordEncoder passwordEncoder() {
        logger.info("I am in SpringSecurityConfig");
        // 明文+随机盐值》加密存储
        return new BCryptPasswordEncoder();
    }

    @Resource
    UserDetailsService userDetailsService;



    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(WhiteUrlUtils.getProperties("whiteUrl.properties"));
    }

    /**
     * 认证管理器： 1. 认证信息（用户名，密码）
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    // 配置文件参数
    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private AuthenticationSuccessHandler customAuthenticationSuccessHandler;

    @Autowired
    private AuthenticationFailureHandler customAuthenticationFailureHandler;

    @Autowired
    DataSource dataSource;

    @Autowired
    private InvalidSessionStrategy invalidSessionStrategy;

    /**
     * 当同个用户session数量超过指定值之后 ,会调用这个实现类
     */
    @Autowired
    private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;


    @Autowired
    private AuthorizeConfigurerManager authorizeConfigurerManager;

    // 校验手机验证码
    @Autowired private MobileValidateFilter mobileValidateFilter;

    // 校验手机号是否存在，就是手机号认证
    @Autowired private MobileAuthenticationConfig mobileAuthenticationConfig;

    // 校验openid
    @Autowired private WechatValidateFilter wechatValidateFilter;

    // 校验openid是否存在，就是微信登录
    @Autowired private WechatAuthenticationConfig wechatAuthenticationConfig;

    /**
     * 当你认证成功之后 ，springsecurity它会重写向到你上一次请求上
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.addFilterBefore(mobileValidateFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(wechatValidateFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                // 表单登录方式
                .formLogin()
                .loginProcessingUrl(
                        securityProperties
                                .getAuthentication()
                                // 登录表单提交处理url, 默认是/login
                                .getLoginUrl())
                .usernameParameter(
                        // 默认的是 username
                        securityProperties.getAuthentication().getUsernameParameter())
                .passwordParameter(
                        // 默认的是 password
                        securityProperties.getAuthentication().getPasswordParameter())
                .and()
                // session管理
                .sessionManagement()
                // 当session失效后的处理类
                .invalidSessionStrategy(invalidSessionStrategy)
                // 每个用户在系统中最多可以有多少个session
                .maximumSessions(1)
                // 当用户达到最大session数后，则调用此处的实现
                .expiredSessionStrategy(sessionInformationExpiredStrategy)
                // 当一个用户达到最大session数,则不允许后面再登录
                .maxSessionsPreventsLogin(true)
                .sessionRegistry(sessionRegistry())
                .and()
                .and()
                .logout()
                // 退出清除缓存
                .logoutSuccessHandler(customLogoutHandler)
                // 退出请求路径
                .logoutUrl(securityProperties.getAuthentication().getLogoutUrl())
                // 退出后删除cookie值
                .deleteCookies("JSESSIONID").and()
                .httpBasic();
        ; // 注意不要少了分号

        http.csrf().disable(); // 关闭跨站请求伪造

        // 将手机认证添加到过滤器链上
        http.apply(mobileAuthenticationConfig);
        // 将微信扫码认证添加到过滤器链上
        http.apply(wechatAuthenticationConfig);

        // 将所有的授权配置统一的起来
        authorizeConfigurerManager.configure(http.authorizeRequests());

    }

    /**
     * 退出清除缓存
     */
    @Autowired
    private LogoutSuccessHandler customLogoutHandler;

    /**
     * 为了解决退出重新登录问题
     *
     * @return
     */
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }


    @Bean
    CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
        filter.setFilterProcessesUrl(securityProperties.getAuthentication().getLoginUrl());
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }

}
